// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_TOKEN_H_
#define CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_TOKEN_H_

#include <memory>
#include <string>

#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "content/common/content_export.h"
#include "url/origin.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);

namespace blink {
enum class WebOriginTrialTokenStatus;
}

namespace content {

// The Origin Trials Framework (OT) provides limited access to experimental
// features, on a per-origin basis. This class defines the trial token data
// structure, used to securely provide access to an experimental feature.
//
// Features are defined by string names, provided by the implementers. The OT
// code does not maintain an enum or constant list for feature names. Instead,
// it validates the name provided by the feature implementation against any
// provided tokens.
//
// More documentation on the token format can be found at
// https://docs.google.com/document/d/1v5fi0EUV_QHckVHVF2K4P72iNywnrJtNhNZ6i2NPt0M

class CONTENT_EXPORT TrialToken {
public:
    ~TrialToken();

    // If the string represents a signed well-formed token, a token object is
    // returned, and success is returned in the |out_status| parameter. Otherwise,
    // the |out_status| parameter indicates what was wrong with the string, and
    // nullptr is returned.
    // Note that success does not mean that the token is currently valid, or
    // appropriate for a given origin / feature. It only means that it is
    // correctly formatted and signed by the supplied public key, and can be
    // parsed.
    static std::unique_ptr<TrialToken> From(
        const std::string& token_text,
        base::StringPiece public_key,
        blink::WebOriginTrialTokenStatus* out_status);

    // Returns success if this token is appropriate for use by the given origin
    // and has not yet expired. Otherwise, the return value indicates why the
    // token is not valid.
    blink::WebOriginTrialTokenStatus IsValid(const url::Origin& origin,
        const base::Time& now) const;

    url::Origin origin() { return origin_; }
    bool match_subdomains() const { return match_subdomains_; }
    std::string feature_name() { return feature_name_; }
    base::Time expiry_time() { return expiry_time_; }

protected:
    // Tests can access the Parse method directly to validate it, and so are
    // declared as friends here. All other access to Parse should be made through
    // TrialToken::From, which will always also ensure that there is a valid
    // signature attached to the token.
    friend class TrialTokenTest;
    friend int ::LLVMFuzzerTestOneInput(const uint8_t*, size_t);

    // If the string represents a properly signed and well-formed token, the token
    // payload is returned in the |out_token_payload| parameter and success is
    // returned. Otherwise,the return code indicates what was wrong with the
    // string, and |out_token_payload| is unchanged.
    static blink::WebOriginTrialTokenStatus Extract(
        const std::string& token_text,
        base::StringPiece public_key,
        std::string* out_token_payload);

    // Returns a token object if the string represents a well-formed JSON token
    // payload, or nullptr otherwise.
    static std::unique_ptr<TrialToken> Parse(const std::string& token_payload);

    bool ValidateOrigin(const url::Origin& origin) const;
    bool ValidateFeatureName(base::StringPiece feature_name) const;
    bool ValidateDate(const base::Time& now) const;

    static bool ValidateSignature(base::StringPiece signature_text,
        const std::string& data,
        base::StringPiece public_key);

private:
    TrialToken(const url::Origin& origin,
        bool match_subdomains,
        const std::string& feature_name,
        uint64_t expiry_timestamp);

    // The origin for which this token is valid. Must be a secure origin.
    url::Origin origin_;

    // Indicates if the token should match all subdomains of the origin.
    bool match_subdomains_;

    // The name of the experimental feature which this token enables.
    std::string feature_name_;

    // The time until which this token should be considered valid.
    base::Time expiry_time_;
};

} // namespace content

#endif // CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_TOKEN_H_
